package luke.audio.processor;

import java.util.Arrays;

/**
 * PCM音频处理器
 * 提供PCM音频数据的各种处理功能，如变速、叠加等
 */
public class PCMAudioProcessor {

    // 音频参数
    private static final int SAMPLE_RATE = 44100; // 采样率
    private static final int CHANNELS = 2; // 声道数（立体声）
    private static final int BYTES_PER_SAMPLE = 2; // 每个样本的字节数（16位）
    private static final int BYTES_PER_FRAME = CHANNELS * BYTES_PER_SAMPLE; // 每帧字节数（立体声16位=4字节）

    /**
     * 在PCM数据前后添加静音时长
     *
     * @param pcmData       原始PCM数据
     * @param startSilenceMs 开头静音时长（毫秒）
     * @param endSilenceMs   结尾静音时长（毫秒）
     * @return 添加静音后的PCM数据
     */
    public static byte[] addSilenceToPcm(byte[] pcmData, int startSilenceMs, int endSilenceMs) {
        if (pcmData == null || pcmData.length == 0) {
            return pcmData;
        }

        // 计算静音数据的字节长度
        int startSilenceFrames = (int) (startSilenceMs * SAMPLE_RATE / 1000.0);
        int endSilenceFrames = (int) (endSilenceMs * SAMPLE_RATE / 1000.0);
        
        int startSilenceBytes = startSilenceFrames * BYTES_PER_FRAME;
        int endSilenceBytes = endSilenceFrames * BYTES_PER_FRAME;

        // 创建新的PCM数据数组，包含原始数据和静音数据
        byte[] newPcmData = new byte[pcmData.length + startSilenceBytes + endSilenceBytes];

        // 填充开头静音（默认为0，不需要额外操作，因为数组初始化为0）
        
        // 复制原始PCM数据到中间位置
        System.arraycopy(pcmData, 0, newPcmData, startSilenceBytes, pcmData.length);

        // 填充结尾静音（默认为0，不需要额外操作）

        return newPcmData;
    }


    /**
     * 在指定位置将声音PCM数据叠加到目标PCM数据上
     *
     * @param audioData 目标音频数据
     * @param soundPcm  声音PCM数据
     * @param position  叠加位置（字节索引）
     */
    public static void overlayPcmSound(byte[] audioData, byte[] soundPcm, int position) {
        // 确保位置有效且对齐到立体声帧边界
        if (position < 0 || position >= audioData.length || position % BYTES_PER_FRAME != 0) {
            System.err.println("[PCM_PROCESSOR] 叠加位置无效: " + position + " (数据长度: " + audioData.length +
                    ", 对齐: " + (position % BYTES_PER_FRAME) + ")");
            return;
        }

        // 计算可以叠加的字节数（确保不会越界）
        int overlayLength = Math.min(soundPcm.length, audioData.length - position);

        // 确保叠加长度是帧对齐的
        overlayLength = (overlayLength / BYTES_PER_FRAME) * BYTES_PER_FRAME;

        // 使用更平滑的叠加方式（避免简单的样本值相加）
        for (int i = 0; i < overlayLength; i += BYTES_PER_FRAME) {
            // 确保不会越界
            if (position + i + (BYTES_PER_FRAME - 1) >= audioData.length) {
                break;
            }

            // 处理左声道样本
            // 获取原始左声道样本值（小端序16位）
            int originalLeftValue = (audioData[position + i] & 0xFF) | ((audioData[position + i + 1] & 0xFF) << 8);
            if (originalLeftValue > 32767)
                originalLeftValue -= 65536; // 转换为有符号值

            // 获取声音左声道样本值
            int soundLeftValue = (soundPcm[i] & 0xFF) | ((soundPcm[i + 1] & 0xFF) << 8);
            if (soundLeftValue > 32767)
                soundLeftValue -= 65536; // 转换为有符号值

            // 使用混合算法而不是简单的相加
            double mixedLeftValue = originalLeftValue + soundLeftValue * 0.5;

            // 限制在有效范围内
            if (mixedLeftValue > 32767)
                mixedLeftValue = 32767;
            if (mixedLeftValue < -32768)
                mixedLeftValue = -32768;

            // 转换回无符号值并写回
            audioData[position + i] = (byte) (((int) mixedLeftValue) & 0xFF);
            audioData[position + i + 1] = (byte) ((((int) mixedLeftValue) >> 8) & 0xFF);

            // 处理右声道样本
            // 获取原始右声道样本值（小端序16位）
            int originalRightValue = (audioData[position + i + 2] & 0xFF) | ((audioData[position + i + 3] & 0xFF) << 8);
            if (originalRightValue > 32767)
                originalRightValue -= 65536; // 转换为有符号值

            // 获取声音右声道样本值
            int soundRightValue = (soundPcm[i + 2] & 0xFF) | ((soundPcm[i + 3] & 0xFF) << 8);
            if (soundRightValue > 32767)
                soundRightValue -= 65536; // 转换为有符号值

            // 使用混合算法而不是简单的相加
            double mixedRightValue = originalRightValue + soundRightValue * 0.5;

            // 限制在有效范围内
            if (mixedRightValue > 32767)
                mixedRightValue = 32767;
            if (mixedRightValue < -32768)
                mixedRightValue = -32768;

            // 转换回无符号值并写回
            audioData[position + i + 2] = (byte) (((int) mixedRightValue) & 0xFF);
            audioData[position + i + 3] = (byte) ((((int) mixedRightValue) >> 8) & 0xFF);
        }
    }

    /**
     * 调整PCM数据的音量
     *
     * @param pcmData PCM音频数据
     * @param volume  音量大小（0.0-1.0）
     * @return 调整音量后的PCM数据
     */
    public static byte[] adjustPCMVolume(byte[] pcmData, float volume) {
        if (pcmData == null || pcmData.length == 0) {
            return pcmData;
        }

        // 限制音量范围
        volume = Math.max(0.0f, Math.min(1.0f, volume));

        // 创建输出数据副本
        byte[] outputData = pcmData.clone();

        // 调整每个样本的音量
        for (int i = 0; i < outputData.length - 1; i += 2) {
            // 获取样本值（小端序16位）
            int sampleValue = (outputData[i] & 0xFF) | ((outputData[i + 1] & 0xFF) << 8);
            if (sampleValue > 32767)
                sampleValue -= 65536; // 转换为有符号值

            // 应用音量调整
            double adjustedValue = sampleValue * volume;

            // 限制在有效范围内
            if (adjustedValue > 32767)
                adjustedValue = 32767;
            if (adjustedValue < -32768)
                adjustedValue = -32768;

            // 转换回无符号值并写回
            short shortValue = (short) adjustedValue;
            outputData[i] = (byte) (shortValue & 0xFF);
            outputData[i + 1] = (byte) ((shortValue >> 8) & 0xFF);
        }

        return outputData;
    }

    /**
     * 对PCM音频数据进行变速不变调处理（基于重采样）
     * 简单实用，效果自然
     *
     * @param pcmData PCM音频数据
     * @param speed   变速倍数（例如：1.5表示1.5倍速，0.75表示0.75倍速）
     * @return 变速不变调后的PCM音频数据
     */
    public static byte[] changeSpeedPreservePitch(byte[] pcmData, double speed) {
        if (pcmData == null || pcmData.length == 0) {
            return pcmData;
        }

        // 限制速度范围
        speed = Math.max(0.25, Math.min(4.0, speed));

        // 如果速度为1.0，则无需处理
        if (Math.abs(speed - 1.0) < 0.0001) {
            return pcmData.clone();
        }

        // 将字节数据转换为short样本数组（更容易处理）
        short[][] samples = bytesToShortSamples(pcmData);
        short[] leftChannel = samples[0];
        short[] rightChannel = samples[1];

        // 分别处理左右声道
        short[] processedLeft = resamplePreservePitch(leftChannel, speed);
        short[] processedRight = resamplePreservePitch(rightChannel, speed);

        // 将处理后的short样本转换回字节数据
        return shortSamplesToBytes(processedLeft, processedRight);
    }

    /**
     * 重采样并保持音调
     */
    private static short[] resamplePreservePitch(short[] samples, double speed) {
        int inputLength = samples.length;
        int outputLength = (int) (inputLength / speed);

        short[] output = new short[outputLength];

        // 使用线性插值进行重采样
        for (int i = 0; i < outputLength; i++) {
            double inputIndex = i * speed;
            int index1 = (int) Math.floor(inputIndex);
            int index2 = Math.min(index1 + 1, inputLength - 1);

            double fraction = inputIndex - index1;

            // 线性插值
            double interpolated = samples[index1] * (1 - fraction) + samples[index2] * fraction;

            // 限制在short范围内
            if (interpolated > 32767) interpolated = 32767;
            if (interpolated < -32768) interpolated = -32768;

            output[i] = (short) Math.round(interpolated);
        }

        return output;
    }

    /**
     * 将字节数据转换为short样本数组
     */
    private static short[][] bytesToShortSamples(byte[] pcmData) {
        int numSamples = pcmData.length / BYTES_PER_FRAME;
        short[] leftChannel = new short[numSamples];
        short[] rightChannel = new short[numSamples];

        for (int i = 0; i < numSamples; i++) {
            int byteIndex = i * BYTES_PER_FRAME;

            // 左声道（小端序）
            leftChannel[i] = (short) ((pcmData[byteIndex + 1] & 0xFF) << 8 | (pcmData[byteIndex] & 0xFF));

            // 右声道（小端序）
            rightChannel[i] = (short) ((pcmData[byteIndex + 3] & 0xFF) << 8 | (pcmData[byteIndex + 2] & 0xFF));
        }

        return new short[][]{leftChannel, rightChannel};
    }

    /**
     * 将short样本数组转换回字节数据
     */
    private static byte[] shortSamplesToBytes(short[] leftChannel, short[] rightChannel) {
        int numSamples = Math.min(leftChannel.length, rightChannel.length);
        byte[] output = new byte[numSamples * BYTES_PER_FRAME];

        for (int i = 0; i < numSamples; i++) {
            int byteIndex = i * BYTES_PER_FRAME;

            // 左声道（小端序）
            output[byteIndex] = (byte) (leftChannel[i] & 0xFF);
            output[byteIndex + 1] = (byte) ((leftChannel[i] >> 8) & 0xFF);

            // 右声道（小端序）
            output[byteIndex + 2] = (byte) (rightChannel[i] & 0xFF);
            output[byteIndex + 3] = (byte) ((rightChannel[i] >> 8) & 0xFF);
        }

        return output;
    }

    /**
     * 高质量变速不变调处理（使用重叠相加法）
     * 效果更好，适合对音质要求高的场景
     */
    public static byte[] changeSpeedHighQuality(byte[] pcmData, double speed) {
        if (pcmData == null || pcmData.length == 0) {
            return pcmData;
        }

        speed = Math.max(0.25, Math.min(4.0, speed));

        if (Math.abs(speed - 1.0) < 0.0001) {
            return pcmData.clone();
        }

        short[][] samples = bytesToShortSamples(pcmData);
        short[] leftChannel = samples[0];
        short[] rightChannel = samples[1];

        // 使用重叠相加法获得更好质量
        short[] processedLeft = overlapAddResample(leftChannel, speed);
        short[] processedRight = overlapAddResample(rightChannel, speed);

        return shortSamplesToBytes(processedLeft, processedRight);
    }

    /**
     * 重叠相加重采样（质量更好）
     */
    private static short[] overlapAddResample(short[] samples, double speed) {
        int inputLength = samples.length;
        int outputLength = (int) (inputLength / speed);

        // 窗口大小和重叠
        int windowSize = 1024;
        int overlap = 256;
        int hopSize = windowSize - overlap;

        short[] output = new short[outputLength + windowSize];

        int inputPos = 0;
        int outputPos = 0;

        // 创建汉宁窗
        double[] window = new double[windowSize];
        for (int i = 0; i < windowSize; i++) {
            window[i] = 0.5 * (1 - Math.cos(2 * Math.PI * i / (windowSize - 1)));
        }

        while (inputPos < inputLength - windowSize && outputPos < outputLength) {
            // 提取当前窗口
            double[] currentWindow = new double[windowSize];
            for (int i = 0; i < windowSize; i++) {
                if (inputPos + i < inputLength) {
                    currentWindow[i] = samples[inputPos + i] * window[i];
                }
            }

            // 添加到输出（重叠相加）
            for (int i = 0; i < windowSize; i++) {
                int outputIndex = outputPos + i;
                if (outputIndex < output.length) {
                    double mixed = output[outputIndex] + currentWindow[i];
                    // 限制范围
                    if (mixed > 32767) mixed = 32767;
                    if (mixed < -32768) mixed = -32768;
                    output[outputIndex] = (short) Math.round(mixed);
                }
            }

            inputPos += hopSize;
            outputPos += (int) (hopSize / speed);
        }

        // 截断到正确长度
        return Arrays.copyOf(output, outputLength);
    }
    
}